package com.kj.tms.common.utils.calx;


import com.kj.tms.common.bean.dto.ExternalRelationDto;
import com.kj.tms.common.bean.dto.WithinRelationDto;
import com.kj.tms.common.utils.StringUtil;
import com.kj.tms.common.utils.calx.sheet.Sheet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: ledger
 * @Package: com.hongtu.tms.ledger.calx.factory.calx
 * @ClassName: Calx
 * @Description: [Calx 入口类]
 * @Author: [zhuyongjie]
 * @CreateDate: 2018/4/18 18:00
 * @UpdateUser: []
 * @UpdateDate: 2018/4/18 18:00
 * @UpdateRemark: []
 * @Version: 1.0
 * Copyright: 北京宏图天安 Copyright (c) 2018
 */
public class Calx {
    private Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * 是否正在计算
     */
    private boolean isCalculating = false;
    /**
     * 版本
     */
    private final String version = "1.0.0";
    /**
     * 注册的sheet
     */
    private Map<String, Sheet> sheetRegistry;
    /**
     * sheet数据列表
     */
    private List<Map<String,Object>> dataList;
    /**
     * 表内列表
     */
    private List<WithinRelationDto> winList;
    /**
     * 表间列表
     */
    private List<ExternalRelationDto> extList;
    /**
     * 配置项
     */
    private Map<String,Object> config;
    /**
     * 台账表单编码
     */
    private String formCode;
    /**
     * 前台单元格值改变，触发表间单元格
     */
    private String changeAddress;
    /**
     * changeCellAddress单元格影响单元格
     */
    private List<String> changeCellAddress = new ArrayList<>();

    public Calx(List<Map<String, Object>> dataList,
                List<WithinRelationDto> winList,
                List<ExternalRelationDto> extList,
                String formCode,
                Map<String,Object> config, String changeAddress)
            throws Exception {
        this.dataList = dataList;
        this.winList = winList;
        this.extList = extList;
        this.formCode = formCode;
        this.config = config;
        this.changeAddress = changeAddress;
        this.init();
    }

    public Calx() throws Exception {
        init();
    }

    /**
     * @Method      init
     * @Param
     * @Return      void
     * @Exception
     * @Description [calx 初始化入口]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/18 18:06
     */
    private void init() throws Exception {
        sheetRegistry = new HashMap<String,Sheet>();
        //sheet页唯一标识
       String sheetIdentifier = "";
       /*******************初始化sheet页   start  ********/
       if(StringUtil.isEmptyOrNull(this.formCode)){
           sheetIdentifier = "CALX" + System.currentTimeMillis();
       }else{
           sheetIdentifier = this.formCode;
       }
       this.sheetRegistry.put(sheetIdentifier,
                new Sheet(sheetIdentifier,this.dataList,
                        this.winList,this.extList,
                        this.formCode,this.config,this));

        /*******************初始化sheet页   end  ********/
        //todo:建立单元格和sheet页之间的依赖树
        if(!StringUtil.isEmptyOrNull(this.sheetRegistry)){
            for(Map.Entry<String,Sheet> entry : this.sheetRegistry.entrySet()){
                Sheet sheet = (Sheet) entry.getValue();
                sheet.buildCellDependency();
            }
        }//if end

        //查找单元格相关依赖，用于关联表间计算
        if(!StringUtil.isEmptyOrNull(this.changeAddress)){
            if(!StringUtil.isEmptyOrNull(this.sheetRegistry)){
                for(Map.Entry<String,Sheet> entry : this.sheetRegistry.entrySet()){
                    Sheet sheet = (Sheet) entry.getValue();
                    sheet.buildChangeDependency(this.changeAddress);
                }
            }//if end
        }//if end
        //System.out.println("关联依赖为:" + this.changeCellAddress.toString());
        logger.info("关联依赖为:" + this.changeCellAddress.toString());


        //循环计算sheet页
        for(Map.Entry<String,Sheet> entry : this.sheetRegistry.entrySet()){
            Sheet sheet = entry.getValue();
            boolean checkCircularReference =  (Boolean)sheet.getConfig().get("checkCircularReference");
            if(checkCircularReference){
                //循环依赖检查
                Map<String, Object> reference = sheet.checkCircularReference();
                boolean isCircular = (Boolean)reference.get("isCircular");
                if(isCircular){
                    Object obj = reference.get("cell");
                    if(!StringUtil.isEmptyOrNull(obj)){
                        Cell cell = (Cell)obj;
                        String error = "单元格[" + cell.getAddress()
                                + "]计算公式["
                                + cell.getDataFormula()
                                +"]存在循环依赖，导致计算停止。"
                                + "请检查公式设置!";
                        logger.error(error);
                        throw new Exception(error);
                    }//if end
                }//if end
            }//if end
            //计算
            if((Boolean) sheet.getConfig().get("autoCalculate")){
                sheet.calculate();
            }//if end
            //赋值最终结果值
            sheet.renderComputedValue();
        }//for end
    }//calx.init() en


    /**
     * @Method      destory
     * @Param		formCode
     * @Return      int
     * @Exception
     * @Description [销毁sheet页]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/7 10:23
     */
    public int destory(String formCode){
        int result = 0;
        if(StringUtil.isEmptyOrNull(formCode)){
            //销毁所有sheet页
            if(!StringUtil.isEmptyOrNull(this.sheetRegistry)
                    && this.sheetRegistry.size() > 0){
                result = this.sheetRegistry.size();
                this.sheetRegistry.clear();
            }//if end

        }else {
            if(this.sheetRegistry.containsKey(formCode)){
                Sheet sheet = sheetRegistry.get(formCode);
                result = 1;
                this.sheetRegistry.remove(sheet);
            }//if end
        }//if end
        return result;
    }//destory end

    /**
     * @Method      getSheet
     * @Param		sheetIdentifier
     * @Return      com.hongtu.tms.ledger.calx.factory.sheet.Sheet
     * @Exception
     * @Description [获取指定sheet页]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/7 14:42
     */
    public Sheet getSheet(String sheetIdentifier){
        if(StringUtil.isEmptyOrNull(sheetIdentifier)){
            return null;
        }else{
            if(!StringUtil.isEmptyOrNull(this.sheetRegistry)
                    && this.sheetRegistry.size() > 0){
                if(this.sheetRegistry.containsKey(sheetIdentifier)){
                    return this.sheetRegistry.get(sheetIdentifier);
                }else{
                    return null;
                }
            }else {
                return null;
            }
        }//if end
    }//getSheet end

    /**
     * @Method      getCell
     * @Param		address
     * @Param		formCode
     * @Return      com.hongtu.tms.ledger.calx.factory.cell.Cell
     * @Exception
     * @Description [获取指定台账的单元格]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/7 15:20
     */
    public Cell getCell(String address,String formCode){
        if(!StringUtil.isEmptyOrNull(this.sheetRegistry)
                && this.sheetRegistry.size() > 0){
            if(this.sheetRegistry.containsKey(formCode)){
                return this.sheetRegistry.get(formCode).getCell(address);
            }
        }//if end
        return null;
    }//getCell end

    /**
     * @Method      setValue
     * @Param		address
     * @Param		value
     * @Param		formCode
     * @Return      void
     * @Exception
     * @Description [设置指定台账单元格的值并执行计算]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/7 15:23
     */
    public void setValue(String address,String value,String formCode)
            throws Exception {
        Sheet sheet = this.sheetRegistry.get(formCode);
        Cell cell = sheet.getCell(address);
        cell.setValue(value).renderComputedValue();
        //计算
        if((Boolean) sheet.getConfig().get("autoCalculate")){
            sheet.calculate();
        }//if end
    }//setValue end

    public Map<String, Sheet> getSheetRegistry() {
        return sheetRegistry;
    }

    public void setSheetRegistry(Map<String, Sheet> sheetRegistry) {
        this.sheetRegistry = sheetRegistry;
    }

    public List<String> getChangeCellAddress() {
        return changeCellAddress;
    }

    public void setChangeCellAddress(List<String> changeCellAddress) {
        this.changeCellAddress = changeCellAddress;
    }
}
